home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pico / browse.c < prev    next >
C/C++ Source or Header  |  1996-03-16  |  45KB  |  1,964 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: browse.c,v 4.59 1996/03/16 23:32:58 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Routines to support file browser in pico and Pine composer
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  *
  30.  * NOTES:
  31.  *
  32.  *   Misc. thoughts (mss, 5 Apr 92)
  33.  * 
  34.  *      This is supposed to be just a general purpose browser, equally
  35.  *    callable from either pico or the pine composer.  Someday, it could
  36.  *     even be used to "wrap" the unix file business for really novice 
  37.  *      users.  The stubs are here for renaming, copying, creating directories,
  38.  *      deleting, undeleting (thought is delete just moves files to 
  39.  *      ~/.pico.deleted directory or something and undelete offers the 
  40.  *      files in there for undeletion: kind of like the mac trashcan).
  41.  *
  42.  *   Nice side effects
  43.  *
  44.  *      Since the full path name is always maintained and referencing ".." 
  45.  *      stats the path stripped of its trailing name, the unpleasantness of 
  46.  *      symbolic links is hidden.  
  47.  *
  48.  *   Fleshed out the file managements stuff (mss, 24 June 92)
  49.  *
  50.  *
  51.  */
  52. #include <stdio.h>
  53. #include <ctype.h>
  54. #include <errno.h>
  55. #include "osdep.h"
  56. #include "pico.h"
  57. #include "estruct.h"
  58. #include "edef.h"
  59. #include "efunc.h"
  60.  
  61. #ifndef    _WINDOWS
  62.  
  63. #if    defined(bsd) || defined(lnx)
  64. extern int errno;
  65. #endif
  66.  
  67.  
  68. /*
  69.  * directory cell structure
  70.  */
  71. struct fcell {
  72.     char *fname;                /* file name            */
  73.     unsigned mode;                /* file's mode           */
  74.     char size[16];                /* file's size in s    */
  75.     struct fcell *next;
  76.     struct fcell *prev;
  77. };
  78.  
  79.  
  80. /*
  81.  * master browser structure
  82.  */
  83. static struct bmaster {
  84.     struct fcell *head;                /* first cell in list  */
  85.     struct fcell *top;                /* cell in top left    */
  86.     struct fcell *current;            /* currently selected  */
  87.     int    longest;                /* longest file name   */
  88.     int       fpl;                    /* file names per line */
  89.     int    cpf;                    /* chars / file / line */
  90.     char   dname[NLINE];            /* this dir's name     */
  91.     char   *names;                /* malloc'd name array */
  92. } *gmp;                        /* global master ptr   */
  93.  
  94.  
  95. /*
  96.  * title for exported browser display
  97.  */
  98. static    char    *browser_title = NULL;
  99.  
  100.  
  101. #ifdef    ANSI
  102.     struct bmaster *getfcells(char *);
  103.     int    PaintCell(int, int, int, struct fcell *, int);
  104.     int    PaintBrowser(struct bmaster *, int, int *, int *);
  105.     int    BrowserKeys(void);
  106.     int    layoutcells(struct bmaster *);
  107.     int    percdircells(struct bmaster *);
  108.     int    PlaceCell(struct bmaster *, struct fcell *, int *, int *);
  109.     int    zotfcells(struct fcell *);
  110.     int    zotmaster(struct bmaster **);
  111.     struct fcell *FindCell(struct bmaster *, char *);
  112.     int    sisin(char *, char *);
  113.     int    BrowserAnchor(char *);
  114.     void   ClearBrowserScreen(void);
  115.     void   BrowserRunChild(char *);
  116.     int    LikelyASCII(char *);
  117. #else
  118.     struct bmaster *getfcells();
  119.     int    PaintCell();
  120.     int    PaintBrowser();
  121.     int    BrowserKeys();
  122.     int    percdircells();
  123.     int    PlaceCell();
  124.     int    zotfcells();
  125.     int    zotmaster();
  126.     struct fcell *FindCell();
  127.     int    sisin();
  128.     int    BrowserAnchor();
  129.     void   ClearBrowserScreen();
  130.     void   BrowserRunChild();
  131.     int    LikelyASCII();
  132. #endif
  133.  
  134.  
  135. static    KEYMENU menu_browse[] = {
  136.     {"?", "Get Help", KS_SCREENHELP},    {NULL, NULL, KS_NONE},
  137.     {NULL, NULL, KS_NONE},        {"-", "Prev Pg", KS_PREVPAGE},
  138.     {"D", "Delete", KS_NONE},        {"C","Copy", KS_NONE},
  139.     {NULL, NULL, KS_NONE},        {NULL, NULL, KS_NONE},
  140.     {"W", "Where is", KS_NONE},        {"Spc", "Next Pg", KS_NEXTPAGE},
  141.     {"R", "Rename", KS_NONE},        {NULL, NULL, KS_NONE}
  142. };
  143. #define    QUIT_KEY    1
  144. #define    EXEC_KEY    2
  145. #define    SELECT_KEY    7
  146. #define    PICO_KEY    11
  147.  
  148.  
  149. /*
  150.  * Default pager used by the stand-alone file browser.
  151.  */
  152. #define    BROWSER_PAGER    ((gmode & MDFKEY) ? "pine -k -F" : "pine -F")
  153.  
  154.  
  155. /*
  156.  * function key mappings for callable browser
  157.  */
  158. static int  bfmappings[2][12][2] = { { { F1,  '?'},    /* stand alone */
  159.                        { F2,  NODATA },    /* browser function */
  160.                        { F3,  'q'},    /* key mappings... */
  161.                        { F4,  'v'},
  162.                        { F5,  'l'},
  163.                        { F6,  'w'},
  164.                        { F7,  '-'},
  165.                        { F8,  ' '},
  166.                        { F9,  'd'},
  167.                        { F10, 'r'},
  168.                        { F11, 'c'},
  169.                        { F12, 'e'} },
  170.                      { { F1,  '?'},    /* callable browser */
  171.                        { F2,  NODATA },    /* function key */
  172.                        { F3,  'e'},    /* mappings... */
  173.                        { F4,  's'},
  174.                        { F5,  NODATA },
  175.                        { F6,  'w'},
  176.                        { F7,  '-'},
  177.                        { F8,  ' '},
  178.                        { F9,  'd'},
  179.                        { F10, 'r'},
  180.                        { F11, 'c'},
  181.                        { F12, NODATA } } };
  182.  
  183.  
  184. /*
  185.  * Browser help for pico (pine composer help handed to us by pine)
  186.  */
  187. static char *BrowseHelpText[] = {
  188. "Help for Browse Command",
  189. "  ",
  190. "\tPico's file browser is used to select a file from the",
  191. "\tfile system for inclusion in the edited text.",
  192. "  ",
  193. "~\tBoth directories and files are displayed.  Press ~S",
  194. "~\tor ~R~e~t~u~r~n to select a file or directory.  When a file",
  195. "\tis selected during the \"Read File\" command, it is",
  196. "\tinserted into edited text.  Answering \"yes\" to the",
  197. "\tverification question after a directory is selected causes",
  198. "\tthe contents of that directory to be displayed for selection.",
  199. "  ",
  200. "\tThe file named \"..\" is special, and means the \"parent\"",
  201. "\tof the directory being displayed.  Select this directory",
  202. "\tto move upward in the directory tree.",
  203. "  ",
  204. "End of Browser Help.",
  205. "  ",
  206. NULL
  207. };
  208.  
  209. static char *sa_BrowseHelpText[] = {
  210. "Help for File Browser",
  211. "  ",
  212. "\tThe File Browser is used to display and manipulate files.",
  213. "  ",
  214. "~\tBoth directories and files are displayed.  Press ~V",
  215. "~\tor ~R~e~t~u~r~n to display the selected directory or view a",
  216. "~\ttext file's contents.  Other commands are available to",
  217. "~\tEdit, Copy, Rename, and Delete the selected (highlighted)",
  218. "~\tfile.",
  219. "  ",
  220. "\tThe file named \"..\" is special, and means the \"parent\"",
  221. "\tof the directory being displayed.  Select this directory",
  222. "\tto move upward in the directory tree.",
  223. "  ",
  224. "End of File Browser Help.",
  225. "  ",
  226. NULL
  227. };
  228.  
  229.  
  230.  
  231. /*
  232.  * pico_file_browse - Exported version of FileBrowse below.
  233.  */
  234. pico_file_browse(pdata, dir, fn, sz, flags)
  235.     PICO *pdata;
  236.     char *dir, *fn, *sz;
  237.     int   flags;
  238. {
  239.     int  rv;
  240.     char title_buf[64];
  241.  
  242.     Pmaster = pdata;
  243.     gmode = pdata->pine_flags | MDEXTFB;
  244.     km_popped = 0;
  245.  
  246.     /* only init screen bufs for display and winch handler */
  247.     if(!vtinit())
  248.       return(-1);
  249.  
  250.     if(Pmaster){
  251.     term.t_mrow = Pmaster->menu_rows;
  252.     if(Pmaster->oper_dir)
  253.       strncpy(opertree, Pmaster->oper_dir, NLINE);
  254.     
  255.     if(*opertree)
  256.       fixpath(opertree, NLINE);
  257.     }
  258.  
  259.     /* install any necessary winch signal handler */
  260.     ttresize();
  261.  
  262.     sprintf(title_buf, "%s FILE", pdata->pine_anchor);
  263.     set_browser_title(title_buf);
  264.     rv = FileBrowse(dir, fn, sz, flags);
  265.     set_browser_title(NULL);
  266.     vttidy();            /* clean up tty handling */
  267.     zotdisplay();        /* and display structures */
  268.     Pmaster = NULL;        /* and global config structure */
  269.     return(rv);
  270. }
  271.  
  272.  
  273.  
  274. /*
  275.  * FileBrowse - display contents of given directory dir
  276.  *
  277.  *        intput:  
  278.  *             dir points to initial dir to browse.
  279.  *             fn  initial file name. 
  280.  *
  281.  *         returns:
  282.  *                   dir points to currently selected directory (without
  283.  *            trailing file system delimiter)
  284.  *                   fn  points to currently selected file
  285.  *                   sz  points to size of file if ptr passed was non-NULL
  286.  *             flags
  287.  *
  288.  *                   1 if a file's been selected
  289.  *                   0 if no files seleted
  290.  *                  -1 if there where problems
  291.  */
  292. FileBrowse(dir, fn, sz, flags)
  293. char *dir, *fn, *sz;            /* dir, name and optional size */
  294. int   flags;
  295. {
  296.     int status, i, j, c, new_c;
  297.     int row, col, crow, ccol;
  298.     char *p, child[NLINE], tmp[NLINE];
  299.     struct bmaster *mp, *getfcells();
  300.     struct fcell *tp;
  301. #ifdef MOUSE
  302.     MOUSEPRESS mousep;
  303. #endif
  304.  
  305.     child[0] = '\0';
  306.  
  307.     if((gmode&MDTREE) && !in_oper_tree(dir)){
  308.     emlwrite("\007Can't read outside of %s in restricted mode", opertree);
  309.     sleep(2);
  310.     return(0);
  311.     }
  312.  
  313.     /* build contents of cell structures */
  314.     if((gmp = getfcells(dir)) == NULL)
  315.       return(-1);
  316.  
  317.     /* paint screen */
  318.     PaintBrowser(gmp, 0, &crow, &ccol);
  319.  
  320.     while(1){                        /* the big loop */
  321.     if(!(gmode&MDSHOCUR)){
  322.         crow = term.t_nrow-term.t_mrow;
  323.         ccol = 0;
  324.     }
  325.  
  326.     movecursor(crow, ccol);
  327.  
  328.     if(km_popped){
  329.         km_popped--;
  330.         if(km_popped == 0)
  331.           /* cause bottom three lines to repaint */
  332.           PaintBrowser(gmp, 0, &crow, &ccol);
  333.     }
  334.  
  335.     if(km_popped){  /* temporarily change to cause menu to paint */
  336.         term.t_mrow = 2;
  337.         movecursor(term.t_nrow-2, 0);  /* clear status line */
  338.         peeol();
  339.         BrowserKeys();
  340.         term.t_mrow = 0;
  341.     }
  342.  
  343.     (*term.t_flush)();
  344.  
  345. #ifdef MOUSE
  346.     mouse_in_content(K_MOUSE, -1, -1, 0, 0);
  347.     register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
  348.         term.t_ncol);
  349. #endif
  350.     c = GetKey();
  351. #ifdef    MOUSE
  352.     clear_mfunc(mouse_in_content);
  353. #endif
  354.  
  355.     if(Pmaster){
  356.         if(Pmaster->newmail && (c == NODATA || time_to_check())){
  357.         if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
  358.             if(km_popped){        /* restore display */
  359.             km_popped = 0;
  360.             PaintBrowser(gmp, 0, &crow, &ccol);
  361.             }
  362.  
  363.             clearcursor();
  364.             mlerase();
  365.             (*Pmaster->showmsg)(c);
  366.             mpresf = 1;
  367.         }
  368.  
  369.         clearcursor();
  370.         movecursor(crow, ccol);
  371.         }
  372.     }
  373.     else{
  374.         if(timeout && (c == NODATA || time_to_check()))
  375.           if(pico_new_mail())
  376.         emlwrite("You may possibly have new mail.", NULL);
  377.     }
  378.  
  379.     if(km_popped)
  380.       switch(c){
  381.         case NODATA:
  382.         case (CTRL|'L'):
  383.           km_popped++;
  384.           break;
  385.         
  386.         default:
  387.           /* clear bottom three lines */
  388.           movecursor(term.t_nrow-2, 0);
  389.           peeol();
  390.           movecursor(term.t_nrow-1, 0);
  391.           peeol();
  392.           movecursor(term.t_nrow, 0);
  393.           peeol();
  394.           break;
  395.       }
  396.  
  397.     if(c == NODATA)                /* GetKey timed out */
  398.       continue;
  399.  
  400.     if(mpresf){                /* blast old messages */
  401.         if(mpresf++ > MESSDELAY){        /* every few keystrokes */
  402.         mlerase();
  403.         }
  404.         }
  405.  
  406.                         /* process commands */
  407.     switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
  408.  
  409.       case K_PAD_RIGHT:            /* move right */
  410.       case (CTRL|'@'):
  411.       case (CTRL|'F'):            /* forward  */
  412.       case 'n' :
  413.       case 'N' :
  414.         if(gmp->current->next == NULL){
  415.         (*term.t_beep)();
  416.         break;
  417.         }
  418.  
  419.         PlaceCell(gmp, gmp->current, &row, &col);
  420.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  421.         gmp->current = gmp->current->next;
  422.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  423.         PaintBrowser(gmp, 1, &crow, &ccol);
  424.         }
  425.         else{
  426.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  427.         crow = row;
  428.         ccol = col;
  429.         }
  430.         break;
  431.  
  432.       case K_PAD_LEFT:                /* move left */
  433.       case (CTRL|'B'):                /* back */
  434.       case 'p' :
  435.       case 'P' :
  436.         if(gmp->current->prev == NULL){
  437.         (*term.t_beep)();
  438.         break;
  439.         }
  440.  
  441.         PlaceCell(gmp, gmp->current, &row, &col);
  442.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  443.         gmp->current = gmp->current->prev;
  444.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  445.         PaintBrowser(gmp, 1, &crow, &ccol);
  446.         }
  447.         else{
  448.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  449.         crow = row;
  450.         ccol = col;
  451.         }
  452.         break;
  453.  
  454.       case (CTRL|'A'):                /* beginning of line */
  455.         tp = gmp->current;
  456.         i = col;
  457.         while(i > 0){
  458.         i -= gmp->cpf;
  459.         if(tp->prev != NULL)
  460.           tp = tp->prev;
  461.         }
  462.         PlaceCell(gmp, gmp->current, &row, &col);
  463.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  464.         gmp->current = tp;
  465.         if(PlaceCell(gmp, tp, &row, &col)){
  466.         PaintBrowser(gmp, 1, &crow, &ccol);
  467.         }
  468.         else{
  469.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  470.         crow = row;
  471.         ccol = col;
  472.         }
  473.         break;
  474.  
  475.       case (CTRL|'E'):                /* end of line */
  476.         tp = gmp->current;
  477.         i = col + gmp->cpf;
  478.         while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
  479.         i += gmp->cpf;
  480.         if(tp->next != NULL)
  481.           tp = tp->next;
  482.         }
  483.  
  484.         PlaceCell(gmp, gmp->current, &row, &col);
  485.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  486.         gmp->current = tp;
  487.         if(PlaceCell(gmp, tp, &row, &col)){
  488.         PaintBrowser(gmp, 1, &crow, &ccol);
  489.         }
  490.         else{
  491.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  492.         crow = row;
  493.         ccol = col;
  494.         }
  495.         break;
  496.  
  497.       case (CTRL|'V'):                /* page forward */
  498.       case ' ':
  499.       case K_PAD_NEXTPAGE :
  500.       case K_PAD_END :
  501.         tp = gmp->top;
  502.         i = term.t_nrow - term.t_mrow - 2;
  503.  
  504.         while(i-- && tp->next != NULL){
  505.         j = 0;
  506.         while(++j <= gmp->fpl  && tp->next != NULL)
  507.           tp = tp->next;
  508.         }
  509.  
  510.         if(tp == NULL)
  511.           continue;
  512.  
  513.         PlaceCell(gmp, gmp->current, &row, &col);
  514.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  515.         gmp->current = tp;
  516.         if(PlaceCell(gmp, tp, &row, &col)){
  517.         PaintBrowser(gmp, 1, &crow, &ccol);
  518.         }
  519.         else{
  520.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  521.         crow = row;
  522.         ccol = col;
  523.         }
  524.         break;
  525.  
  526.       case '-' :
  527.       case (CTRL|'Y'):                /* page backward */
  528.       case K_PAD_PREVPAGE :
  529.       case K_PAD_HOME :
  530.         tp = gmp->top;
  531.         i = term.t_nrow - term.t_mrow - 4;
  532.         while(i-- && tp != NULL){
  533.         j = gmp->fpl;
  534.         while(j-- && tp != NULL)
  535.           tp = tp->prev;
  536.         }
  537.  
  538.         if(tp || (gmp->current != gmp->top)){    /* clear old hilite */
  539.         PlaceCell(gmp, gmp->current, &row, &col);
  540.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  541.         }
  542.  
  543.         if(tp)                    /* new page ! */
  544.         gmp->current = tp;
  545.         else if(gmp->current != gmp->top)        /* goto top of page */
  546.         gmp->current = gmp->top;
  547.         else                    /* do nothing */
  548.           continue;
  549.  
  550.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  551.         PaintBrowser(gmp, 1, &crow, &ccol);
  552.         }
  553.         else{
  554.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  555.         crow = row;
  556.         ccol = col;
  557.         }
  558.  
  559.         break;
  560.  
  561.       case K_PAD_DOWN :
  562.       case (CTRL|'N'):                /* next */
  563.         tp = gmp->current;
  564.         i = gmp->fpl;
  565.         while(i--){
  566.         if(tp->next == NULL){
  567.             (*term.t_beep)();
  568.             break;
  569.         }
  570.         else
  571.           tp = tp->next;
  572.         }
  573.         if(i != -1)                    /* can't go down */
  574.           break;
  575.  
  576.         PlaceCell(gmp, gmp->current, &row, &col);
  577.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  578.         gmp->current = tp;
  579.         if(PlaceCell(gmp, tp, &row, &col)){
  580.         PaintBrowser(gmp, 1, &crow, &ccol);
  581.         }
  582.         else{
  583.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  584.         crow = row;
  585.         ccol = col;
  586.         }
  587.         break;
  588.  
  589.       case K_PAD_UP :
  590.       case (CTRL|'P'):                /* previous */
  591.         tp = gmp->current;
  592.         i = gmp->fpl;
  593.         while(i-- && tp)
  594.           tp = tp->prev;
  595.  
  596.         if(tp == NULL)
  597.           break;
  598.  
  599.         PlaceCell(gmp, gmp->current, &row, &col);
  600.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  601.         gmp->current = tp;
  602.         if(PlaceCell(gmp, tp, &row, &col)){
  603.         PaintBrowser(gmp, 1, &crow, &ccol);
  604.         }
  605.         else{
  606.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  607.         crow = row;
  608.         ccol = col;
  609.         }
  610.         break;
  611.  
  612. #ifdef MOUSE        
  613.       case K_MOUSE:
  614.         mouse_get_last (NULL, &mousep);
  615.         if (mousep.doubleclick) {
  616.         goto Selected;
  617.         }
  618.         else {
  619.         row = mousep.row -= 2;        /* Adjust for header*/
  620.         col = mousep.col;
  621.         i = row * gmp->fpl + (col / gmp->cpf);    /* Count from top */
  622.         tp = gmp->top;                /* start at top. */
  623.         for (; i > 0 && tp != NULL; --i)    /* Count cells. */
  624.           tp = tp->next;
  625.         if (tp != NULL) {            /* Valid cell? */
  626.             PlaceCell(gmp, gmp->current, &row, &col);
  627.             PaintCell(row, col, gmp->cpf, gmp->current, 0);
  628.             gmp->current = tp;
  629.             if(PlaceCell(gmp, tp, &row, &col)){
  630.             PaintBrowser(gmp, 1, &crow, &ccol);
  631.             }
  632.             else{
  633.             PaintCell(row, col, gmp->cpf, gmp->current, 1);
  634.             crow = row;
  635.             ccol = col;
  636.             }
  637.         }
  638.             }
  639.         break;
  640. #endif
  641.  
  642.       case 'e':                    /* exit or edit */
  643.       case 'E':
  644.         if(gmode&MDBRONLY){                /* run "pico" */
  645.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP,
  646.             gmp->current->fname);
  647.         /* make sure selected isn't a directory or executable */
  648.         if(!LikelyASCII(child)){
  649.             emlwrite("Can't edit non-text file.  Try Launch.", NULL);
  650.             break;
  651.         }
  652.  
  653.         sprintf(tmp, "pico%s%s%s %s",
  654.             (gmode & MDFKEY) ? " -f" : "",
  655.             (gmode & MDSHOCUR) ? " -g" : "",
  656.             (gmode & MDMOUSE) ? " -m" : "",
  657.             child);
  658.         BrowserRunChild(tmp);        /* spawn pico */
  659.         PaintBrowser(gmp, 0, &crow, &ccol);    /* redraw browser */
  660.         }
  661.         else{
  662.         zotmaster(&gmp);
  663.         return(0);
  664.         }
  665.  
  666.         break;
  667.  
  668.       case 'q':                        /* user exits wrong */
  669.       case 'Q':
  670.         if(gmode&MDBRONLY){
  671.         zotmaster(&gmp);
  672.         return(0);
  673.         }
  674.  
  675.         emlwrite("\007Unknown command '%c'", (void *)c);
  676.         break;
  677.  
  678.       case 'l':                        /* run Command */
  679.       case 'L':
  680.         if(!(gmode&MDBRONLY)){
  681.         emlwrite("\007Unknown command '%c'", (void *)c);
  682.         break;
  683.         }
  684.  
  685. /* add subcommands to invoke pico and insert selected filename */
  686. /* perhaps: add subcmd to scroll command history */
  687.  
  688.         tmp[0] = '\0';
  689.         i = 0;
  690.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP,
  691.             gmp->current->fname);
  692.         while(!i){
  693.         static EXTRAKEYS opts[] = {
  694.             {"^X", "Add Name", CTRL|'X', KS_NONE},
  695.             {NULL, NULL, 0, KS_NONE},
  696.         };
  697.  
  698.         status = mlreply("Command to execute: ",
  699.                  tmp, NLINE, QNORML, opts);
  700.         switch(status){
  701.           case HELPCH:
  702.             emlwrite("\007No help yet!", NULL);
  703. /* remove break and sleep after help text is installed */
  704.             sleep(3);
  705.             break;
  706.           case (CTRL|'X'):
  707.             strcat(tmp, child);
  708.             break;
  709.           case (CTRL|'L'):
  710.             PaintBrowser(gmp, 0, &crow, &ccol);
  711.             break;
  712.           case ABORT:
  713.             emlwrite("Command cancelled", NULL);
  714.             i++;
  715.             break;
  716.           case FALSE:
  717.           case TRUE:
  718.             i++;
  719.  
  720.             if(tmp[0] == '\0'){
  721.             emlwrite("No command specified", NULL);
  722.             break;
  723.             }
  724.  
  725.             BrowserRunChild(tmp);
  726.             PaintBrowser(gmp, 0, &crow, &ccol);
  727.             break;
  728.           default:
  729.             break;
  730.         }
  731.         }
  732.  
  733.         BrowserKeys();
  734.         break;
  735.  
  736.       case 'd':                    /* delete */
  737.       case 'D':
  738.         if(gmp->current->mode == FIODIR){
  739. /* BUG: if dir is empty it should be deleted */
  740.         emlwrite("\007Can't delete a directory", NULL);
  741.         break;
  742.         }
  743.  
  744.         if(gmode&MDSCUR){                /* not allowed! */
  745.         emlwrite("Delete not allowed in restricted mode",NULL);
  746.         break;
  747.         }
  748.  
  749.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, 
  750.             gmp->current->fname);
  751.  
  752.         i = 0;
  753.         while(i++ < 2){        /* verify twice!! */
  754.         if(i == 1){
  755.             if(fexist(child, "w", (long *)NULL) != FIOSUC)
  756.               strcpy(tmp, "File is write protected! OVERRIDE");
  757.             else
  758.               sprintf(tmp, "Delete file \"%.*s\"", NLINE - 20, child);
  759.         }
  760.         else
  761.           strcpy(tmp, "File CANNOT be UNdeleted!  Really delete");
  762.  
  763.         if((status = mlyesno(tmp, FALSE)) != TRUE){
  764.             emlwrite((status ==  ABORT)
  765.                    ? "Delete Cancelled"
  766.                    : "File Not Deleted",
  767.                  NULL);
  768.             break;
  769.         }
  770.         }
  771.  
  772.         if(status == TRUE){
  773.         if(unlink(child) < 0){
  774.             emlwrite("Delete Failed: %s", errstr(errno));
  775.         }
  776.         else{            /* fix up pointers and redraw */
  777.             tp = gmp->current;
  778.             if(tp->next){
  779.             gmp->current = tp->next;
  780.             if(tp->next->prev = tp->prev)
  781.               tp->prev->next = tp->next;
  782.             }
  783.             else if(tp->prev) {
  784.             gmp->current = tp->prev;
  785.             if(tp->prev->next = tp->next)
  786.               tp->next->prev = tp->prev;
  787.             }
  788.  
  789.             if(tp == gmp->head)
  790.               gmp->head = tp->next;
  791.  
  792.             tp->fname = NULL;
  793.             tp->next = tp->prev = NULL;
  794.             if(tp != gmp->current)
  795.               free((char *) tp);
  796.  
  797.             if((tp = FindCell(gmp, gmp->current->fname)) != NULL){
  798.             gmp->current = tp;
  799.             PlaceCell(gmp, gmp->current, &row, &col);
  800.             }
  801.                 
  802.             PaintBrowser(gmp, 1, &crow, &ccol);
  803.             mlerase();
  804.         }
  805.         }
  806.  
  807.         BrowserKeys();
  808.         break;
  809.  
  810.       case '?':                    /* HELP! */
  811.       case (CTRL|'G'):
  812.         if(term.t_mrow == 0){
  813.         if(km_popped == 0){
  814.             km_popped = 2;
  815.             break;
  816.         }
  817.         }
  818.  
  819.         if(Pmaster)
  820.           (*Pmaster->helper)(Pmaster->browse_help,
  821.                  "Help for Browsing", 1);
  822.         else if(gmode&MDBRONLY)
  823.           pico_help(sa_BrowseHelpText, "Browser Help", 1);
  824.         else
  825.           pico_help(BrowseHelpText, "Help for Browsing", 1);
  826.         /* fall thru to repaint everything */
  827.  
  828.       case (CTRL|'L'):
  829.         PaintBrowser(gmp, 0, &crow, &ccol);
  830.         break;
  831.  
  832.       case 'c':                    /* copy */
  833.       case 'C':
  834.         if(gmp->current->mode == FIODIR){
  835.         emlwrite("\007Can't copy a directory", NULL);
  836.         break;
  837.         }
  838.  
  839.         if(gmode&MDSCUR){                /* not allowed! */
  840.         emlwrite("Copy not allowed in restricted mode",NULL);
  841.         break;
  842.         }
  843.  
  844.         i = 0;
  845.         child[0] = '\0';
  846.  
  847.         while(!i){
  848.  
  849.         switch(status=mlreply("Name of new copy: ", child, NLINE,
  850.                       QFFILE, NULL)){
  851.           case HELPCH:
  852.             emlwrite("\007No help yet!", NULL);
  853. /* remove break and sleep after help text is installed */
  854.             sleep(3);
  855.             break;
  856.           case (CTRL|'L'):
  857.             PaintBrowser(gmp, 0, &crow, &ccol);
  858.             break;
  859.           case ABORT:
  860.             emlwrite("Make Copy Cancelled", NULL);
  861.             i++;
  862.             break;
  863.           case FALSE:
  864.             i++;
  865.             mlerase();
  866.             break;
  867.           case TRUE:
  868.             i++;
  869.  
  870.             if(child[0] == '\0'){
  871.             emlwrite("No destination, file not copied", NULL);
  872.             break;
  873.             }
  874.  
  875.             if(!strcmp(gmp->current->fname, child)){
  876.             emlwrite("\007Can't copy file on to itself!", NULL);
  877.             break;
  878.             }
  879.  
  880.             strcpy(tmp, child);         /* add full path! */
  881.             sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, tmp);
  882.  
  883.             if((status = fexist(child, "w", (long *)NULL)) == FIOSUC){
  884.             sprintf(tmp,"File \"%.*s\" exists! OVERWRITE",
  885.                 NLINE - 20, child);
  886.             if((status = mlyesno(tmp, 0)) != TRUE){
  887.                 emlwrite((status == ABORT)
  888.                       ? "Make copy cancelled" 
  889.                       : "File Not Renamed",
  890.                      NULL);
  891.                 break;
  892.             }
  893.             }
  894.             else if(status != FIOFNF){
  895.             fioperr(status, child);
  896.             break;
  897.             }
  898.  
  899.             sprintf(tmp, "%s%c%s", gmp->dname, C_FILESEP, 
  900.                 gmp->current->fname);
  901.  
  902.             if(copy(tmp, child) < 0){
  903.             /* copy()  will report any error messages */
  904.             break;
  905.             }
  906.             else{            /* highlight new file */
  907.             emlwrite("File copied to %s", child);
  908.  
  909.             if((p = strrchr(child, C_FILESEP)) == NULL){
  910.                 emlwrite("Problems refiguring browser", NULL);
  911.                 break;
  912.             }
  913.  
  914.             *p = '\0';
  915.             strcpy(tmp, (p == child) ? S_FILESEP: child);
  916.  
  917.             /*
  918.              * new file in same dir? if so, refigure files
  919.              * and redraw...
  920.              */
  921.             if(!strcmp(tmp, gmp->dname)){ 
  922.                 strcpy(child, gmp->current->fname);
  923.                 if((mp = getfcells(gmp->dname)) == NULL)
  924.                   /* getfcells should explain what happened */
  925.                   break;
  926.  
  927.                 zotmaster(&gmp);
  928.                 gmp = mp;
  929.                 if((tp = FindCell(gmp, child)) != NULL){
  930.                 gmp->current = tp;
  931.                 PlaceCell(gmp, gmp->current, &row, &col);
  932.                 }
  933.  
  934.                 PaintBrowser(gmp, 1, &crow, &ccol);
  935.             }
  936.             }
  937.             break;
  938.           default:
  939.             break;
  940.         }
  941.         }
  942.         BrowserKeys();
  943.         break;
  944.  
  945.       case 'r':                    /* rename */
  946.       case 'R':
  947.         i = 0;
  948.         child[0] = '\0';
  949.  
  950.         if(!strcmp(gmp->current->fname, "..")){
  951.         emlwrite("\007Can't rename \"..\"", NULL);
  952.         break;
  953.         }
  954.  
  955.         if(gmode&MDSCUR){                /* not allowed! */
  956.         emlwrite("Rename not allowed in restricted mode",NULL);
  957.         break;
  958.         }
  959.  
  960.         while(!i){
  961.  
  962.         switch(status=mlreply("Rename file to: ", child, NLINE, QFFILE,
  963.                       NULL)){
  964.           case HELPCH:
  965.             emlwrite("\007No help yet!", NULL);
  966. /* remove break and sleep after help text is installed */
  967.             sleep(3);
  968.             break;
  969.           case (CTRL|'L'):
  970.             PaintBrowser(gmp, 0, &crow, &ccol);
  971.             break;
  972.           case ABORT:
  973.             emlwrite("Rename cancelled", NULL);
  974.             i++;
  975.             break;
  976.           case FALSE:
  977.           case TRUE:
  978.             i++;
  979.  
  980.             if(child[0] == '\0' || status == FALSE){
  981.             mlerase();
  982.             break;
  983.             }
  984.  
  985.             strcpy(tmp, child);
  986.             sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, tmp);
  987.  
  988.             status = fexist(child, "w", (long *)NULL);
  989.             if(status == FIOSUC || status == FIOFNF){
  990.             if(status == FIOSUC){
  991.                 sprintf(tmp,"File \"%.*s\" exists! OVERWRITE",
  992.                     NLINE - 20, child);
  993.  
  994.                 if((status = mlyesno(tmp, FALSE)) != TRUE){
  995.                 emlwrite((status ==  ABORT)
  996.                       ? "Rename cancelled"
  997.                       : "Not Renamed",
  998.                      NULL);
  999.                 break;
  1000.                 }
  1001.             }
  1002.  
  1003.             sprintf(tmp, "%s%c%s", gmp->dname, C_FILESEP, 
  1004.                 gmp->current->fname);
  1005.  
  1006.             if(rename(tmp, child) < 0){
  1007.                 emlwrite("Rename Failed: %s", errstr(errno));
  1008.             }
  1009.             else{
  1010.                 if((p = strrchr(child, C_FILESEP)) == NULL){
  1011.                 emlwrite("Problems refiguring browser", NULL);
  1012.                 break;
  1013.                 }
  1014.                 
  1015.                 *p = '\0';
  1016.                 strcpy(tmp, (p == child) ? S_FILESEP: child);
  1017.  
  1018.                 if((mp = getfcells(tmp)) == NULL)
  1019.                   /* getfcells should explain what happened */
  1020.                   break;
  1021.  
  1022.                 zotmaster(&gmp);
  1023.                 gmp = mp;
  1024.  
  1025.                 if((tp = FindCell(gmp, ++p)) != NULL){
  1026.                 gmp->current = tp;
  1027.                 PlaceCell(gmp, gmp->current, &row, &col);
  1028.                 }
  1029.  
  1030.                 PaintBrowser(gmp, 1, &crow, &ccol);
  1031.                 mlerase();
  1032.             }
  1033.             }
  1034.             else{
  1035.             fioperr(status, child);
  1036.             }
  1037.             break;
  1038.           default:
  1039.             break;
  1040.         }
  1041.         }
  1042.         BrowserKeys();
  1043.         break;
  1044.  
  1045.       case 'v':                    /* stand-alone */
  1046.       case 'V':                    /* browser "view" */
  1047.       case 's':                    /* user "select" */
  1048.       case 'S':
  1049.       case (CTRL|'M'):
  1050.       Selected:
  1051.  
  1052.         if((toupper(new_c) == 'S' && (gmode&MDBRONLY))
  1053.            || (toupper(new_c) == 'V' && !(gmode&MDBRONLY))){
  1054.         (*term.t_beep)();
  1055.         goto Default;
  1056.         }
  1057.  
  1058.         if(gmp->current->mode == FIODIR){
  1059.         *child = '\0';
  1060.         strcpy(tmp, gmp->dname);
  1061.         p = gmp->current->fname;
  1062.         if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
  1063.             if((p=strrchr(tmp, C_FILESEP)) != NULL){
  1064.             *p = '\0';
  1065.  
  1066.             if((gmode&MDTREE) && !in_oper_tree(tmp)){
  1067.                 emlwrite(
  1068.                    "\007Can't visit parent in restricted mode",
  1069.                    NULL);
  1070.                 break;
  1071.             }
  1072.  
  1073.             strcpy(child, &p[1]);
  1074.             if
  1075. #if defined(DOS) || defined(OS2)
  1076.               (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
  1077. #else
  1078.               (p == tmp)
  1079. #endif
  1080.               {    /* is it root? */
  1081. #if defined(DOS) || defined(OS2)
  1082.                   if(*child)
  1083.                 strcat(tmp, S_FILESEP);
  1084. #else
  1085.                   if(*child)
  1086.                     strcpy(tmp, S_FILESEP);
  1087. #endif
  1088.                   else{
  1089.                   emlwrite("\007Can't move up a directory",
  1090.                        NULL);
  1091.                   break;
  1092.                   }
  1093.               }
  1094.             }
  1095.         }
  1096.         else{
  1097.             if(tmp[1] != '\0')        /* were in root? */
  1098.               strcat(tmp, S_FILESEP);
  1099.             strcat(tmp, gmp->current->fname);
  1100.         }
  1101.  
  1102.         if((mp = getfcells(tmp)) == NULL)
  1103.           /* getfcells should explain what happened */
  1104.           break;
  1105.  
  1106.         zotmaster(&gmp);
  1107.         gmp = mp;
  1108.         tp  = NULL;
  1109.  
  1110.         if(*child){
  1111.             if((tp = FindCell(gmp, child)) != NULL){
  1112.             gmp->current = tp;
  1113.             PlaceCell(gmp, gmp->current, &row, &col);
  1114.             }
  1115.             else
  1116.               emlwrite("\007Problem finding dir \"%s\"",child);
  1117.         }
  1118.  
  1119.         PaintBrowser(gmp, 0, &crow, &ccol);
  1120.         if(!*child)
  1121.           emlwrite("Select/View \".. parent dir\" to return to previous directory.",
  1122.                NULL);
  1123.  
  1124.         break;
  1125.         }
  1126.         else if(gmode&MDBRONLY){
  1127.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, 
  1128.             gmp->current->fname);
  1129.  
  1130.         if(LikelyASCII(child)){
  1131.             char *pg = (char *) getenv("PAGER");
  1132.  
  1133.             sprintf(tmp, "%s %s", pg ? pg : BROWSER_PAGER, child);
  1134.             BrowserRunChild(tmp);
  1135.             PaintBrowser(gmp, 0, &crow, &ccol);
  1136.         }
  1137.  
  1138.         break;
  1139.         }
  1140.         else{                /* just return */
  1141.         strcpy(dir, gmp->dname);
  1142.         strcpy(fn, gmp->current->fname);
  1143.         if(sz != NULL)            /* size uninteresting */
  1144.           strcpy(sz, gmp->current->size);
  1145.  
  1146.         zotmaster (&gmp);
  1147.         return (1);
  1148.         }
  1149.         break;
  1150.  
  1151.       case 'w':                /* Where is */
  1152.       case 'W':
  1153.       case (CTRL|'W'):
  1154.         i = 0;
  1155.  
  1156.         while(!i){
  1157.  
  1158.         switch(readpattern("File name to find")){
  1159.           case HELPCH:
  1160.             emlwrite("\007No help yet!", NULL);
  1161. /* remove break and sleep after help text is installed */
  1162.             sleep(3);
  1163.             break;
  1164.           case (CTRL|'L'):
  1165.             PaintBrowser(gmp, 0, &crow, &ccol);
  1166.             break;
  1167.           case (CTRL|'Y'):        /* first first cell */
  1168.             for(tp = gmp->top; tp->prev; tp = tp->prev)
  1169.               ;
  1170.  
  1171.             i++;
  1172.             /* fall thru to repaint */
  1173.           case (CTRL|'V'):
  1174.             if(!i){
  1175.             do{
  1176.                 tp = gmp->top;
  1177.                 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
  1178.                   break;
  1179.  
  1180.                 while(i-- && tp->next){
  1181.                 j = 0;
  1182.                 while(++j <= gmp->fpl  && tp->next)
  1183.                   tp = tp->next;
  1184.                 }
  1185.  
  1186.                 if(i < 0)
  1187.                   gmp->top = tp;
  1188.             }
  1189.             while(tp->next);
  1190.  
  1191.             emlwrite("Searched to end of directory", NULL);
  1192.             }
  1193.             else
  1194.               emlwrite("Searched to start of directory", NULL);
  1195.  
  1196.             if(tp){
  1197.             PlaceCell(gmp, gmp->current, &row, &col);
  1198.             PaintCell(row, col, gmp->cpf, gmp->current, 0);
  1199.             gmp->current = tp;
  1200.             if(PlaceCell(gmp, gmp->current, &row, &col)){
  1201.                 PaintBrowser(gmp, 1, &crow, &ccol);
  1202.             }
  1203.             else{
  1204.                 PaintCell(row, col, gmp->cpf, gmp->current, 1);
  1205.                 crow = row;
  1206.                 ccol = col;
  1207.             }
  1208.             }
  1209.  
  1210.             i++;            /* make sure we jump out */
  1211.             break;
  1212.           case ABORT:
  1213.             emlwrite("Whereis cancelled", NULL);
  1214.             i++;
  1215.             break;
  1216.           case FALSE:
  1217.             mlerase();
  1218.             i++;
  1219.             break;
  1220.           case TRUE:
  1221.             if((tp = FindCell(gmp, pat)) != NULL){
  1222.             PlaceCell(gmp, gmp->current, &row, &col);
  1223.             PaintCell(row, col, gmp->cpf, gmp->current, 0);
  1224.             gmp->current = tp;
  1225.  
  1226.             if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
  1227.                 PaintBrowser(gmp, 1, &crow, &ccol);
  1228.             }
  1229.             else{
  1230.                 PaintCell(row, col, gmp->cpf, gmp->current, 1);
  1231.                 crow = row;
  1232.                 ccol = col;
  1233.             }
  1234.             mlerase();
  1235.             }
  1236.             else
  1237.               emlwrite("\"%s\" not found", pat);
  1238.  
  1239.             i++;
  1240.             break;
  1241.           default:
  1242.             break;
  1243.         }
  1244.         }
  1245.  
  1246.         BrowserKeys();
  1247.         break;
  1248.  
  1249.       case (CTRL|'Z'):
  1250.         if(gmode&MDSSPD){
  1251.         bktoshell();
  1252.         PaintBrowser(gmp, 0, &crow, &ccol);
  1253.         break;
  1254.         }                    /* fall thru with error! */
  1255.  
  1256.       default:                /* what? */
  1257.       Default:
  1258.         if(c < 0xff)
  1259.           emlwrite("\007Unknown command: '%c'", (void *) c);
  1260.         else if(c & CTRL)
  1261.           emlwrite("\007Unknown command: ^%c", (void *)(c&0xff));
  1262.         else
  1263.           emlwrite("\007Unknown command", NULL);
  1264.       case NODATA:                /* no op */
  1265.         break;
  1266.     }
  1267.     }
  1268. }
  1269.  
  1270.  
  1271.  
  1272. /*
  1273.  * getfcells - make a master browser struct and fill it in
  1274.  *             return NULL if there's a problem.
  1275.  */
  1276. struct bmaster *
  1277. getfcells(dname)
  1278.     char *dname;
  1279. {
  1280.     int  i,                     /* various return codes */
  1281.          nentries = 0;                /* number of dir ents */
  1282.     long l;
  1283.     char *np,                    /* names of files in dir */
  1284.          *dcp,                    /* to add file to path */
  1285.          **filtnames,                /* array filtered names */
  1286.      errbuf[NLINE];
  1287.     struct fcell *ncp,                /* new cell pointer */
  1288.                  *tcp;                /* trailing cell ptr */
  1289.     struct bmaster *mp;
  1290.  
  1291.     errbuf[0] = '\0';
  1292.     if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
  1293.     emlwrite("\007Can't malloc space for master filename cell", NULL);
  1294.     return(NULL);
  1295.     }
  1296.  
  1297.     if(dname[0] == '.' && dname[1] == '\0'){        /* remember this dir */
  1298.     if(!getcwd(mp->dname, 256))
  1299.       mp->dname[0] = '\0';
  1300.     }
  1301.     else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
  1302.     if(!getcwd(mp->dname, 256))
  1303.       mp->dname[0] = '\0';
  1304.     else{
  1305.         if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
  1306.           if(np != mp->dname)
  1307.         *np = '\0';
  1308.     }
  1309.     }
  1310.     else
  1311.       strcpy(mp->dname, dname);
  1312.  
  1313.     mp->head = mp->top = NULL;
  1314.     mp->cpf = mp->fpl = 0;
  1315.     mp->longest = 5;                /* .. must be labeled! */
  1316.  
  1317.     emlwrite("Building file list of %s...", mp->dname);
  1318.  
  1319.     if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf)) == NULL){
  1320.     free((char *) mp);
  1321.     if(*errbuf)
  1322.       emlwrite(errbuf, NULL);
  1323.  
  1324.     return(NULL);
  1325.     }
  1326.  
  1327.     /*
  1328.      * this is the fun part.  build an array of pointers to the fnames we're
  1329.      * interested in (i.e., do any filtering), then pass that off to be
  1330.      * sorted before building list of cells...
  1331.      *
  1332.      * right now default filtering on ".*" except "..", but this could
  1333.      * easily be made a user option later on...
  1334.      */
  1335.     if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
  1336.     emlwrite("\007Can't malloc space for name array", NULL);
  1337.     zotmaster(&mp);
  1338.     return(NULL);
  1339.     }
  1340.  
  1341.     i = 0;                    /* index of filt'd array */
  1342.     np = mp->names;
  1343.     while(nentries--){
  1344.     /*
  1345.      * Filter dot files?  Always filter ".", never filter "..",
  1346.      * and sometimes fitler ".*"...
  1347.      */
  1348.     if(*np == '.' && !(*(np+1) == '.' && *(np+2) == '\0')
  1349.        && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
  1350.         np += strlen(np) + 1;
  1351.         continue;
  1352.     }
  1353.  
  1354.     filtnames[i++] = np;
  1355.  
  1356.     if((l = strlen(np)) > mp->longest)    /* cast l ? */
  1357.       mp->longest = l;            /* remember longest */
  1358.     np += l + 1;                /* advance name pointer */
  1359.  
  1360.     }
  1361.     nentries = i;                /* new # of entries */
  1362.  
  1363.     /* 
  1364.      * sort files case independently
  1365.      */
  1366.     qsort((QSType *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
  1367.  
  1368.     /* 
  1369.      * this is so we use absolute path names for stats.
  1370.      * remember: be careful using dname as directory name, and fix mp->dname
  1371.      * when we're done
  1372.      */
  1373.     dcp = (char *)strchr(mp->dname, '\0');
  1374.     if(dcp == mp->dname || dcp[-1] != C_FILESEP){
  1375.     dcp[0] = C_FILESEP;
  1376.     dcp[1] = '\0';
  1377.     }
  1378.     else
  1379.       dcp--;
  1380.  
  1381.     i = 0;
  1382.     while(nentries--){                /* stat filtered files */
  1383.     /* get a new cell */
  1384.     if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
  1385.         emlwrite("\007Can't malloc cells for browser!", NULL);
  1386.         zotfcells(mp->head);        /* clean up cells */
  1387.         free((char *) filtnames);
  1388.         free((char *) mp);
  1389.         return(NULL);            /* bummer. */
  1390.     }
  1391.     ncp->next = ncp->prev = NULL;
  1392.  
  1393.     if(mp->head == NULL){            /* tie it onto the list */
  1394.         mp->head = mp->top = mp->current = ncp;
  1395.     }
  1396.     else{
  1397.         tcp->next = ncp;
  1398.         ncp->prev = tcp;
  1399.     }
  1400.     tcp = ncp;
  1401.  
  1402.     /* fill in the new cell */
  1403.     ncp->fname = filtnames[i++];
  1404.  
  1405.     strcpy(&dcp[1], ncp->fname);        /* use abolute path! */
  1406.  
  1407.     /* fill in file's mode */
  1408.     switch(fexist(mp->dname, "t", &l)){
  1409.       case FIODIR :
  1410.         ncp->mode = FIODIR;
  1411.         sprintf(ncp->size, "(%sdir)",
  1412.             (ncp->fname[0] == '.' && ncp->fname[1] == '.'
  1413.              && ncp->fname[2] == '\0') ? "parent " : "");
  1414.         break;
  1415.       case FIOSYM :
  1416.         ncp->mode = FIOSYM;
  1417.         strcpy(ncp->size, "--");
  1418.         break;
  1419.       default :
  1420.         ncp->mode = FIOSUC;            /* regular file */
  1421.         strcpy(ncp->size,  prettysz(l));
  1422.         break;
  1423.     }
  1424.     }
  1425.  
  1426.     dcp[(dcp == mp->dname) ? 1 : 0] = '\0';    /* remember to cap dname */
  1427.     free((char *) filtnames);            /* 'n blast filt'd array*/
  1428.  
  1429.     percdircells(mp);
  1430.     layoutcells(mp);
  1431.     return(mp);
  1432. }
  1433.  
  1434.  
  1435.  
  1436. /*
  1437.  * PaintCell - print the given cell at the given location on the display
  1438.  *             the format of a printed cell is:
  1439.  *
  1440.  *                       "<fname>       <size>  "
  1441.  */
  1442. PaintCell(x, y, l, cell, inverted)
  1443. struct fcell *cell;
  1444. int    x, y, l, inverted;
  1445. {
  1446.     char *p;                    /* temp str pointer */
  1447.     int   i = 0,                /* current display count */
  1448.           j, sl, fl;                /* lengths */
  1449.  
  1450.     if(cell == NULL)
  1451.     return(-1);
  1452.  
  1453.     fl = strlen(cell->fname);
  1454.     sl = strlen(cell->size);
  1455.  
  1456.     movecursor(x, y);
  1457.     if(inverted)
  1458.       (*term.t_rev)(1);
  1459.     
  1460.     /* room for fname? */
  1461.     p = (fl+2 > l) ? &cell->fname[fl-(l-2)] : cell->fname;
  1462.     while(*p != '\0'){                /* write file name */
  1463.     pputc(*p++, 0);
  1464.     i++;
  1465.     }
  1466.  
  1467.     if(sl+3 <= l-i){                /* room for size? */
  1468.     j = (l-i)-(sl+2);            /* put space between */
  1469.     i += j;
  1470.     while(j--)                /* file name and size */
  1471.       pputc(' ', 0);
  1472.  
  1473.     p = cell->size;
  1474.     while(*p != '\0'){            /* write file size */
  1475.         pputc(*p++, 0);
  1476.         i++;
  1477.     }
  1478.     }
  1479.  
  1480.     if(inverted)
  1481.       (*term.t_rev)(0);
  1482.  
  1483.     while(i++ < l)                /* pad ending */
  1484.       pputc(' ', 0);
  1485.  
  1486.     return(1);
  1487. }
  1488.  
  1489.  
  1490.  
  1491. /*
  1492.  * PaintBrowse - with the current data, display the browser.  if level == 0 
  1493.  *               paint the whole thing, if level == 1 just paint the cells
  1494.  *               themselves
  1495.  */
  1496. PaintBrowser(mp, level, row, col)
  1497. struct bmaster *mp;
  1498. int level;
  1499. int *row, *col;
  1500. {
  1501.     int i, cl;
  1502.     struct fcell *tp;
  1503.  
  1504.     if(!level){
  1505.     ClearBrowserScreen();
  1506.     BrowserAnchor(mp->dname);
  1507.     }
  1508.  
  1509.     i = 0;
  1510.     tp = mp->top;
  1511.     cl = COMPOSER_TOP_LINE;            /* current display line */
  1512.     while(tp){
  1513.  
  1514.     PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
  1515.  
  1516.     if(tp == mp->current){
  1517.         if(row)
  1518.           *row = cl;
  1519.  
  1520.         if(col)
  1521.           *col = mp->cpf * i;
  1522.     }
  1523.  
  1524.     if(++i >= mp->fpl){
  1525.         i = 0;
  1526.         if(++cl > term.t_nrow-(term.t_mrow+1))
  1527.           break;
  1528.     }
  1529.  
  1530.     tp = tp->next;
  1531.     }
  1532.  
  1533.     if(level){
  1534.     while(cl <= term.t_nrow - (term.t_mrow+1)){
  1535.         if(!i)
  1536.           movecursor(cl, 0);
  1537.         peeol();
  1538.         movecursor(++cl, 0);
  1539.     }
  1540.     }
  1541.     else{
  1542.     BrowserKeys();
  1543.     }
  1544.  
  1545.     return(1);
  1546. }
  1547.  
  1548.  
  1549. /*
  1550.  * BrowserKeys - just paints the keyhelp at the bottom of the display
  1551.  */
  1552. BrowserKeys()
  1553. {
  1554.     menu_browse[QUIT_KEY].name  = (gmode&MDBRONLY) ? "Q" : "E";
  1555.     menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? "Quit" : "Exit Brwsr";
  1556.     if(gmode & MDBRONLY){
  1557.     menu_browse[EXEC_KEY].name  = "L";
  1558.     menu_browse[EXEC_KEY].label = "Launch";
  1559.     menu_browse[SELECT_KEY].name  = "V";
  1560.     menu_browse[SELECT_KEY].label = "[View]";
  1561.     menu_browse[PICO_KEY].name  = "E";
  1562.     menu_browse[PICO_KEY].label = "Edit";
  1563.     }
  1564.     else{
  1565.     menu_browse[SELECT_KEY].name  = "S";
  1566.     menu_browse[SELECT_KEY].label = "[Select]";
  1567.     }
  1568.  
  1569.     wkeyhelp(menu_browse);
  1570. }
  1571.  
  1572.  
  1573. /*
  1574.  * layoutcells - figure out max length of cell and how many cells can 
  1575.  *               go on a line of the display
  1576.  */
  1577. layoutcells(mp)
  1578. struct bmaster *mp;
  1579. {
  1580.     mp->cpf = mp->longest + 12;            /* max chars / file */
  1581.     if(gmode & MDONECOL){
  1582.     mp->fpl = 1;
  1583.     }
  1584.     else{
  1585.     int i = 1;
  1586.  
  1587.     while(i*mp->cpf < term.t_ncol)        /* no force... */
  1588.       i++;                    /* like brute force! */
  1589.  
  1590.     mp->fpl = i - 1;            /* files per line */
  1591.     }
  1592. }
  1593.  
  1594.  
  1595. /*
  1596.  * percdircells - bubble all the directory cells to the top of the
  1597.  *                list.
  1598.  */
  1599. percdircells(mp)
  1600. struct bmaster *mp;
  1601. {
  1602.     struct fcell *dirlp,            /* dir cell list pointer */
  1603.                  *lp, *nlp;            /* cell list ptr and next */
  1604.  
  1605.     dirlp = NULL;
  1606.     for(lp = mp->head; lp; lp = nlp){
  1607.     nlp = lp->next;
  1608.     if(lp->mode == FIODIR){
  1609.         if(lp->prev)            /* clip from list */
  1610.           lp->prev->next = lp->next;
  1611.  
  1612.         if(lp->next)
  1613.           lp->next->prev = lp->prev;
  1614.  
  1615.         if(lp->prev = dirlp){        /* tie it into dir portion */
  1616.         if(lp->next = dirlp->next)
  1617.           lp->next->prev = lp;
  1618.  
  1619.         dirlp->next = lp;
  1620.         dirlp = lp;
  1621.         }
  1622.         else{
  1623.         if((dirlp = lp) != mp->head)
  1624.           dirlp->next = mp->head;
  1625.  
  1626.         if(dirlp->next)
  1627.           dirlp->next->prev = dirlp;
  1628.  
  1629.         mp->head = mp->top = mp->current = dirlp;
  1630.         }
  1631.     }
  1632.     }
  1633. }
  1634.  
  1635.  
  1636. /*
  1637.  * PlaceCell - given a browser master, return row and col of the display that
  1638.  *             it should go.  
  1639.  *
  1640.  *             return 1 if mp->top has changed, x,y relative to new page
  1641.  *             return 0 if otherwise (same page)
  1642.  *             return -1 on error
  1643.  */
  1644. PlaceCell(mp, cp, x, y)
  1645. struct bmaster *mp;
  1646. struct fcell *cp;
  1647. int    *x, *y;
  1648. {
  1649.     int cl = COMPOSER_TOP_LINE;            /* current line */
  1650.     int ci = 0;                    /* current index on line */
  1651.     int rv = 0;
  1652.     int secondtry = 0;
  1653.     struct fcell *tp;
  1654.  
  1655.     /* will cp fit on screen? */
  1656.     tp = mp->top;
  1657.     while(1){
  1658.     if(tp == cp){                /* bingo! */
  1659.         *x = cl;
  1660.         *y = ci * mp->cpf;
  1661.         break;
  1662.     }
  1663.  
  1664.     if((tp = tp->next) == NULL){        /* above top? */
  1665.         if(secondtry++){
  1666.         emlwrite("\007Internal error: can't find fname cell", NULL);
  1667.         return(-1);
  1668.         }
  1669.         else{
  1670.         tp = mp->top = mp->head;    /* try from the top! */
  1671.         cl = COMPOSER_TOP_LINE;
  1672.         ci = 0;
  1673.         rv = 1;
  1674.         continue;            /* start over! */
  1675.         }
  1676.     }
  1677.  
  1678.     if(++ci >= mp->fpl){            /* next line? */
  1679.         ci = 0;
  1680.         if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
  1681.         ci = mp->fpl;            /* tp is at bottom right */
  1682.         while(ci--)            /* find new top */
  1683.           tp = tp->prev;
  1684.         mp->top = tp;
  1685.         ci = 0;
  1686.         cl = COMPOSER_TOP_LINE;        /* keep checking */
  1687.         rv = 1;
  1688.         }
  1689.     }
  1690.  
  1691.     }
  1692.  
  1693.     /* not on display! */
  1694.     return(rv);
  1695.     
  1696. }
  1697.  
  1698.  
  1699. /*
  1700.  * zotfcells - clean up malloc'd cells of file names
  1701.  */
  1702. zotfcells(hp)
  1703. struct fcell *hp;
  1704. {
  1705.     struct fcell *tp;
  1706.  
  1707.     while(hp){
  1708.     tp = hp;
  1709.     hp = hp->next;
  1710.     tp->next = NULL;
  1711.     free((char *) tp);
  1712.     }
  1713. }
  1714.  
  1715.  
  1716. /*
  1717.  * zotmaster - blast the browser master struct
  1718.  */
  1719. zotmaster(mp)
  1720. struct bmaster **mp;
  1721. {
  1722.     zotfcells((*mp)->head);            /* free cells       */
  1723.     free((char *)(*mp)->names);            /* free file names  */
  1724.     free((char *)*mp);                /* free master      */
  1725.     *mp = NULL;                    /* make double sure */
  1726. }
  1727.  
  1728.  
  1729. /*
  1730.  * FindCell - starting from the current cell find the first occurance of 
  1731.  *            the given string wrapping around if necessary
  1732.  */
  1733. struct fcell *FindCell(mp, string)
  1734. struct bmaster *mp;
  1735. char   *string;
  1736. {
  1737.     struct fcell *tp, *fp;
  1738.  
  1739.     if(*string == '\0')
  1740.       return(NULL);
  1741.  
  1742.     fp = NULL;
  1743.     tp = mp->current->next;
  1744.     
  1745.     while(tp && !fp){
  1746.     if(sisin(tp->fname, string))
  1747.       fp = tp;
  1748.     else
  1749.       tp = tp->next;
  1750.     }
  1751.  
  1752.     tp = mp->head;
  1753.     while(tp != mp->current && !fp){
  1754.     if(sisin(tp->fname, string))
  1755.       fp = tp;
  1756.     else
  1757.       tp = tp->next;
  1758.     }
  1759.  
  1760.     return(fp);
  1761. }
  1762.  
  1763.  
  1764. /*
  1765.  * sisin - case insensitive substring matching function
  1766.  */
  1767. sisin(s1, s2)
  1768. char *s1, *s2;
  1769. {
  1770.     register int j;
  1771.  
  1772.     while(*s1){
  1773.     j = 0;
  1774.     while(toupper(s1[j]) == toupper(s2[j]))
  1775.       if(s2[++j] == '\0')            /* bingo! */
  1776.         return(1);
  1777.  
  1778.     s1++;
  1779.     }
  1780.     return(0);
  1781. }
  1782.  
  1783.  
  1784. /*
  1785.  * set_browser_title - 
  1786.  */
  1787. set_browser_title(s)
  1788. char *s;
  1789. {
  1790.     browser_title = s;
  1791. }
  1792.  
  1793.  
  1794. /*
  1795.  * BrowserAnchor - draw the browser's anchor line.
  1796.  */
  1797. BrowserAnchor(dir)
  1798. char *dir;
  1799. {
  1800.     register char *p;
  1801.     register int  i, j, l;
  1802.     char          buf[NLINE];
  1803.  
  1804.     movecursor(0, 0);
  1805.     (*term.t_rev)(1);
  1806.  
  1807.     i = 0;
  1808.     l = strlen(dir);
  1809.     j = (term.t_ncol-(l+16))/2;
  1810.  
  1811.     if(browser_title)
  1812.       sprintf(buf, "   %s", browser_title);
  1813.     else if(Pmaster)
  1814.       sprintf(buf, "   PINE %s", Pmaster->pine_version);
  1815.     else
  1816.       sprintf(buf,"   UW PICO(tm) %s", (gmode&MDBRONLY) ? "BROWSER" : version);
  1817.  
  1818.     p = buf;
  1819.     while(*p){
  1820.     pputc(*p++, 0);
  1821.     i++;
  1822.     }
  1823.  
  1824.     if(l > term.t_ncol - i - 21){        /* fit dir name on line */
  1825.     p = dir;
  1826.     while((p = strchr(p, C_FILESEP)) && (l-(p-dir)) > term.t_ncol-i-21)
  1827.       p++;
  1828.  
  1829.     if(!*p)                    /* no suitable length! */
  1830.       p = &dir[l-(term.t_ncol-i-19)];
  1831.  
  1832.     sprintf(buf, "%s Dir ...%s", (gmode&MDBRONLY) ? "" : " BROWSER  ", p);
  1833.     }
  1834.     else 
  1835.       sprintf(buf,"%s  Dir: %s", (gmode&MDBRONLY) ? "" : " BROWSER  ", dir);
  1836.  
  1837.     if(i < j)                    /* keep it centered */
  1838.       j = j - i;                /* as long as we can */
  1839.     else
  1840.       j = ((term.t_ncol-i)-((int)strlen(p)+15))/2;
  1841.  
  1842.     while(j-- && i++)
  1843.       pputc(' ', 0);
  1844.  
  1845.     p = buf;
  1846.     while(i++ < term.t_ncol && *p)        /* show directory */
  1847.       pputc(*p++, 0);
  1848.  
  1849.     while(i++ < term.t_ncol)
  1850.       pputc(' ', 0);
  1851.  
  1852.     (*term.t_rev)(0);
  1853. }
  1854.  
  1855.  
  1856. /*
  1857.  * ResizeBrowser - handle a resize event
  1858.  */
  1859. ResizeBrowser()
  1860. {
  1861.     if(gmp){
  1862.     layoutcells(gmp);
  1863.     PaintBrowser(gmp, 0, NULL, NULL);
  1864.     return(1);
  1865.     }
  1866.     else
  1867.       return(0);
  1868. }
  1869.  
  1870.  
  1871. void
  1872. ClearBrowserScreen()
  1873. {
  1874.     int i;
  1875.  
  1876.     for(i = 0; i <= term.t_nrow; i++){        /* clear screen */
  1877.     movecursor(i, 0);
  1878.     peeol();
  1879.     }
  1880. }
  1881.  
  1882.  
  1883. void
  1884. BrowserRunChild(child)
  1885.     char *child;
  1886. {
  1887.     int  status;
  1888.     char tmp[NLINE];
  1889.  
  1890.     ClearBrowserScreen();
  1891.     movecursor(0, 0);
  1892.     (*term.t_close)();
  1893.     fflush(stdout);
  1894.     status = system(child);
  1895.     (*term.t_open)();
  1896.     /* complain about non-zero exit status */
  1897.     if((status >> 8) & 0xff){
  1898.  
  1899.     movecursor(term.t_nrow - 1, 0);
  1900.     sprintf(tmp, "[ \"%.30s\" exit with error value: %d ]",
  1901.         child, (status >> 8) & 0xff);
  1902.     pputs(tmp, 1);
  1903.     movecursor(term.t_nrow, 0);
  1904.     pputs("[ Hit RETURN to continue ]", 1);
  1905.     fflush(stdout);
  1906.  
  1907.     while(GetKey() != (CTRL|'M')){
  1908.         (*term.t_beep)();
  1909.         fflush(stdout);
  1910.     }
  1911.     }
  1912. }
  1913. #endif    /* _WINDOWS */
  1914.  
  1915.  
  1916. /*
  1917.  * LikelyASCII - make a rough guess as to the displayability of the
  1918.  *         given file.
  1919.  */
  1920. int
  1921. LikelyASCII(file)
  1922.     char *file;
  1923. {
  1924. #define    LA_TEST_BUF    1024
  1925. #define    LA_LINE_LIMIT    128
  1926. #if defined(DOS) || defined(OS2)
  1927. #define    MODE    "rb"
  1928. #else
  1929. #define    MODE    "r"
  1930. #endif
  1931.     int           n, i, line, rv = FALSE;
  1932.     unsigned char  buf[LA_TEST_BUF];
  1933.     FILE      *fp;
  1934.  
  1935.     if(fp = fopen(file, "rb")){
  1936.     clearerr(fp);
  1937.     if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
  1938.        || !ferror(fp)){
  1939.         /*
  1940.          * If we don't hit any newlines in a reasonable number,
  1941.          * LA_LINE_LIMIT, of characters or the file contains NULLs,
  1942.          * bag out...
  1943.          */
  1944.         rv = TRUE;
  1945.         for(i = line = 0; i < n; i++)
  1946.           if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
  1947.          || !buf[i]){
  1948.           rv = FALSE;
  1949.           emlwrite("Can't display non-text file.  Try \"Launch\".",
  1950.                NULL);
  1951.           break;
  1952.           }
  1953.     }
  1954.     else
  1955.       emlwrite("Can't read file: %s", file);
  1956.  
  1957.     fclose(fp);
  1958.     }
  1959.     else
  1960.       emlwrite("Can't open file: %s", file);
  1961.  
  1962.     return(rv);
  1963. }
  1964.